#include "ExternalBallisticsSimulator.h"

#include <yaml-cpp/yaml.h>

void ExternalBallisticsSimulator::initialize() {
    std::cout << "输入弹头质量(g): ";
    std::cin >> m_shellMass;
    m_shellMass *= 0.001;
    std::cout << "输入初速(m/s): ";
    std::cin >> m_muzzleVelocity;
    std::cout << "输入空气阻力系数: ";
    std::cin >> m_dragCoefficient;
    while (1) {
        char input;
        std::cout << "是否自定义其他参数？(Y/N): ";
        std::cin >> input;
        if (input == 'Y' || input == 'y') {
            std::cout << "输入弹头口径(mm): ";
            std::cin >> m_caliber;
            m_caliber *= 0.001;
            std::cout << "输入空气密度(kg/m^2): ";
            std::cin >> m_AirDensity;
            std::cout << "输入重力加速度(m/s^2): ";
            std::cin >> m_G;
            break;
        } else if (input == 'N' || input == 'n') {
            break;
        }
    }
    m_shellarea = 3.14159 * (m_caliber / 2) * (m_caliber / 2);
    std::cout << "参数输入完成" << std::endl << std::endl;
}

void ExternalBallisticsSimulator::initializefromyaml(const std::string& Filepath) {
    YAML::Node config = YAML::LoadFile(Filepath);
    float shellMass = config["shellMass"].as<float>();
    float muzzleVelocity = config["muzzleVelocity"].as<float>();
    float dragCoefficient = config["dragCoefficient"].as<float>();
    float caliber = config["caliber"].as<float>();
    float AirDensity = config["AirDensity"].as<float>();
    float G = config["G"].as<float>();
    initializeall(shellMass, muzzleVelocity, dragCoefficient, caliber, AirDensity, G);
    m_shellarea = 3.14159 * (m_caliber / 2) * (m_caliber / 2);
    std::cout << "参数输入完成" << std::endl << std::endl;
}

void ExternalBallisticsSimulator::initializeall(float shellMass, float muzzleVelocity, float dragCoefficient,
                                                float caliber, float AirDensity, float G) {
    m_shellMass = 0.001 * static_cast<double>(shellMass);
    m_muzzleVelocity = static_cast<double>(muzzleVelocity);
    m_dragCoefficient = static_cast<double>(dragCoefficient);
    m_caliber = 0.001 * static_cast<double>(caliber);
    m_AirDensity = static_cast<double>(AirDensity);
    m_G = static_cast<double>(G);

    m_shellarea = 3.14159 * (m_caliber / 2) * (m_caliber / 2);
}

void ExternalBallisticsSimulator::stateinitialize() {
    m_shellHorizontalPosition = 0;
    m_shellVerticalPosition = 0;
    m_shellHorizontalVelocity = m_muzzleVelocity;
    m_shellVerticalVelocity = 0;
    m_shellHorizontalAcceleration = 0;
    m_shellVerticalAcceleration = 0;
    m_shellHorizontalForce = 0;
    m_shellVerticalForce = 0;
}

void ExternalBallisticsSimulator::computeForces() {
    double Gforce = m_G * m_shellMass;

    double Velocity =
        sqrt(m_shellHorizontalVelocity * m_shellHorizontalVelocity + m_shellVerticalVelocity * m_shellVerticalVelocity);

    double AirResistance = -1 * 0.5 * m_dragCoefficient * m_AirDensity * Velocity * Velocity * m_shellarea;

    m_shellHorizontalForce = (m_shellHorizontalVelocity / Velocity) * AirResistance;
    m_shellVerticalForce = (m_shellVerticalVelocity / Velocity) * AirResistance - Gforce;
}

void ExternalBallisticsSimulator::computeAcceleration() {
    m_shellHorizontalAcceleration = m_shellHorizontalForce / m_shellMass;
    m_shellVerticalAcceleration = m_shellVerticalForce / m_shellMass;
}
void ExternalBallisticsSimulator::computeVelocity() {
    m_shellHorizontalVelocity += m_shellHorizontalAcceleration * m_timeStep;
    m_shellVerticalVelocity += m_shellVerticalAcceleration * m_timeStep;
}
void ExternalBallisticsSimulator::computePosition() {
    m_shellHorizontalPosition += m_shellHorizontalVelocity * m_timeStep;
    m_shellVerticalPosition += m_shellVerticalVelocity * m_timeStep;
}
std::string ExternalBallisticsSimulator::outputData(int step) {
    double time_ms = m_timeStep * step * 1000;
    double Velocity =
        sqrt(m_shellHorizontalVelocity * m_shellHorizontalVelocity + m_shellVerticalVelocity * m_shellVerticalVelocity);
    double shellEK = 0.5 * m_shellMass * Velocity * Velocity;
#if 0  //完整数据展示
    std::cout << "时间(ms):" << time_ms << ":   弹头位置: " << m_shellPosition << ", 弹头速度: " << m_shellVelocity
              << ", 弹头加速度: " << m_shellAcceleration;
    std::cout << "              拍头位置: " << m_pistonPosition << ", 拍头速度: " << m_pistonVelocity
              << ", 拍头加速度: " << m_pistonAcceleration;
    std::cout << "              当前气体体积/气缸体积: " << volumeratio << ", 当前气压/大气压： " << pressureratio
              << std::endl
              << std::endl;
#else
    std::cout << "时间(ms):" << std::fixed << std::setprecision(1) << std::setfill('0') << std::setw(6) << time_ms;
    std::cout << ":   弹头水平位置: " << std::fixed << std::setprecision(5) << m_shellHorizontalPosition;
    std::cout << ", 弹头垂直位置: " << std::fixed << std::setprecision(3) << m_shellVerticalPosition;
    std::cout << ", 弹速: " << std::fixed << std::setprecision(5) << Velocity;
    std::cout << ", 动能: " << std::fixed << std::setprecision(3) << shellEK;
    std::cout << std::endl;

#endif
    std::ostringstream oss;  // 使用ostringstream来构建字符串

    oss << std::fixed;

    oss << "time_ms:" << std::fixed << std::setprecision(1) << std::setfill('0') << std::setw(6) << time_ms;
    oss << " shellHorizontalPosition:" << std::fixed << std::setprecision(5) << m_shellHorizontalPosition;
    oss << " shellVerticalPosition:" << std::fixed << std::setprecision(3) << m_shellVerticalPosition;
    oss << " Velocity:" << std::fixed << std::setprecision(5) << Velocity;
    oss << " shellEK:" << std::fixed << std::setprecision(3) << shellEK;
    oss << std::endl;

    // 返回oss中的字符串表示
    return oss.str();
}

std::string ExternalBallisticsSimulator::runSim() {
    stateinitialize();
    int step = 0;
    double pos = 0;
    std::string result;
    for (step = 0; ((step < 10000) && (m_shellVerticalPosition > -10) && (m_shellHorizontalVelocity > 5)); step++) {
        computeForces();
        computeAcceleration();
        computeVelocity();
        computePosition();
        if (m_shellHorizontalPosition >= pos) {
            result += outputData(step);
            pos += 5;
        }
    }
    std::cout << "模拟结束!" << std::endl;

    return result;
}
